namespace designpatternsplusplus.decorator.DomainEntities
{
    #region

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Runtime.Serialization;
    using DecoratorParticipants;
    using designpatternsplus.contracts;
    using Support;

    #endregion

    /// <summary>
    /// Just what it sounds like... Technically
    /// a sequence of Concrete Implementations of the 
    /// IProductService contract
    /// But it's a .... shopping basket... 
    /// No relational impedence mismatch here (grin)
    /// </summary>
    [Serializable]
    [DataContract]
    public class ShoppingCart : Collection<IProductService>, IShoppingCart
    {
        readonly Guid _cartId;
        bool Disposed;

        /// <summary>
        /// Initializes a new instance of the <see cref="ShoppingCart"/> class.
        /// </summary>
        internal ShoppingCart()
        {
            
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ShoppingCart"/> class.
        /// </summary>
        /// <param name="itemsSold">The items sold.</param>
        public ShoppingCart(IEnumerable<IProductService> itemsSold)
            : this(Guid.NewGuid())
        {
            foreach (var item in itemsSold)
                Items.Add(item);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ShoppingCart"/> class.
        /// </summary>
        /// <param name="cartSerialNumber">The cart serial number.</param>
        ShoppingCart(Guid cartSerialNumber)
        {
            _cartId = cartSerialNumber;
        }

        /// <summary>
        /// Gets or sets the cart id.
        /// </summary>
        /// <value>The cart id.</value>
        public Guid CartId
        {
            get { return _cartId; }
        }

        #region IShoppingCart Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        /// <summary>
        /// Using the predicate given find
        /// the master item record and add
        /// it to our cart
        /// </summary>
        /// <param name="criteria">The criteria.</param>
        public IShoppingCart ScanProductBarCode(Func<ISkuDetails, bool> criteria)
        {
            var prodFacts = (from ISkuDetails item in CoolCrapMarket.Get.ProductsCarried
                             where criteria(item)
                             select item).FirstOrDefault();


            if (prodFacts.Equals(default(SkuDetails)))
            {
                throw new CouldNotFindProductException
                    ("The criteria you provided was unable to match a product in our inventory");
            }

            return OneToCart(new SellableItem(prodFacts));
        }

        /// <summary>
        /// Usng the literal item we were given, add it to our cart
        /// </summary>
        /// <param name="Category">The item type request in.</param>
        public void AddOne(ItemCategory Category)
        {
            OneToCart(Category.GetFirstMatchingItemOfCategory());
        }

        ///<summary>
        ///</summary>
        ///<param name="skus"></param>
        ///<exception cref="NotImplementedException"></exception>
        public IShoppingCart AutoScanDiscover(IEnumerable<String> skus)
        {
                 foreach (var item in skus)
                 {
                     var item1 = item;
                     ScanProductBarCode(x => x.Sku.Equals(item1));
                 }

            return this;
        }

        #endregion

        /// <summary>
        /// Populates the chained tax instances.
        /// This is the 'magic' of a decorator although
        /// I prefer making this even more abstract
        /// </summary>
        /// <param name="cartIn">The basket in.</param>
        /// 
        internal static IShoppingCart CreateDecoratorChain(IShoppingCart cartIn)
        {
            // Use simple for so as to not modify the iterator
            for (var i = 0; i < cartIn.Count; i++)
                cartIn[i] = cartIn[i].ChainToProductItems();

            return cartIn;
        }

        ShoppingCart OneToCart(IProductService productServiceToSellIn)
        {
            productServiceToSellIn.Quantity = 1;
            Add(productServiceToSellIn);
            return this;
        }

        void Dispose(bool FromDispose)
        {
            if (FromDispose && !Disposed)
            {
                ClearItems();
                Disposed = true;
            }
        }

        /// <summary>
        /// Prepares the specified _cart serial num.
        /// </summary>
        /// <param name="_cartSerialNum">The _cart serial num.</param>
        /// <returns></returns>
        public static IShoppingCart Prepare(Guid _cartSerialNum)
        {
            return new ShoppingCart(_cartSerialNum);
        }
    }
}